﻿Option Strict On
Option Explicit On

Imports System
Imports System.Collections.Generic
Imports System.ComponentModel
Imports System.Drawing
Imports System.IO
Imports System.Xml
Imports System.Reflection
Imports System.Windows.Forms
Imports System.Drawing.Drawing2D

#Region " Enums "

''' <summary>
''' Define the direction of a displayed item
''' </summary>
''' <remarks></remarks>
Friend Enum ItemDirection As Integer
    Up = 0
    Down = 2
    Left = 3
    Right = 4
End Enum

''' <summary>
''' Define the speed of a displayed item
''' </summary>
''' <remarks></remarks>
Friend Enum ItemSpeed As Integer
    Slow = 0
    Idle = 1
    Fast = 2
End Enum

''' <summary>
''' Define the shape of a led
''' </summary>
''' <remarks></remarks>
Friend Enum LedStyle As Integer
    Round = 0
    Square = 1
End Enum

#End Region

#Region " Structures "

''' <summary>
''' Define the composition of a symbol line from the xml "LedLine" node
''' </summary>
''' <remarks></remarks>
Friend Structure LedOnLine

    Public LineNo As Integer 'Line number in the symbol composition
    Public LedOn As String 'String which represents the led On

    ''' <summary>
    ''' Constructor
    ''' </summary>
    ''' <param name="LineNo">Line number</param>
    ''' <param name="LedOn">Line string code</param>
    ''' <remarks></remarks>
    Public Sub New(ByVal LineNo As Integer, ByVal LedOn As String)
        Me.LineNo = LineNo
        Me.LedOn = LedOn
    End Sub

End Structure

''' <summary>
''' Define the composition of a symbol
''' </summary>
''' <remarks></remarks>
Friend Structure LedMatrixSymbol

    Public LedOnMatrix(,) As Boolean 'The led On definition
    Public Description As String 'The description of the symbol
    Public Code As UInteger 'The code of the symbol

    ''' <summary>
    ''' Constructor
    ''' </summary>
    ''' <param name="Code">Code of the symbol</param>
    ''' <param name="Description">Description of the symbol</param>
    ''' <param name="LedOnMatrix">Led On definition</param>
    ''' <remarks></remarks>
    Public Sub New(ByVal Code As UInteger, ByVal Description As String, ByVal LedOnMatrix(,) As Boolean)
        Me.Code = Code
        Me.Description = Description
        Me.LedOnMatrix = LedOnMatrix
    End Sub

End Structure

''' <summary>
''' Define a whole symbols font
''' </summary>
''' <remarks></remarks>
Friend Structure LedMatrixSymbolFont

    Public Name As String 'The font name
    Public SymbolList As List(Of LedMatrixSymbol) 'The list of symbols in the font

    ''' <summary>
    ''' Constructor
    ''' </summary>
    ''' <param name="Name">Font name</param>
    ''' <param name="SymbolList">List of symbols in the font</param>
    ''' <remarks></remarks>
    Public Sub New(ByVal Name As String, ByVal SymbolList As List(Of LedMatrixSymbol))
        Me.Name = Name
        Me.SymbolList = SymbolList
    End Sub

End Structure

''' <summary>
''' Define a collection of symbol fonts
''' </summary>
''' <remarks></remarks>
Friend Structure LedMatrixSymbolFontCollection

    Public FontList As List(Of LedMatrixSymbolFont) 'The list of fonts

    ''' <summary>
    ''' Constructor
    ''' </summary>
    ''' <param name="FontList">List of fonts</param>
    ''' <remarks></remarks>
    Public Sub New(ByVal FontList As List(Of LedMatrixSymbolFont))
        Me.FontList = FontList
    End Sub

End Structure

#End Region

#Region " Support Classes "

''' <summary>
''' Definition of the "physical" properties of a led in the matrix
''' </summary>
''' <remarks></remarks>
Friend Class LedProperty

#Region " Private Members "

    Private m_Location As Point 'Position of the led in the matrix (center of the led)
    Private m_Radius As Integer 'Radius of the led

#End Region

#Region " Constructor "

    ''' <summary>
    ''' Default Constructor
    ''' </summary>
    ''' <remarks></remarks>
    Public Sub New()
        m_Location.X = 0
        m_Location.Y = 0
        m_Radius = 0
    End Sub

#End Region

#Region " Public Properties "

    ''' <summary>
    ''' Position of the led in the matrix (center of the led)
    ''' </summary>
    ''' <value></value>
    ''' <returns></returns>
    ''' <remarks></remarks>
    Public Property Location As Point
        Get
            Return m_Location
        End Get
        Set(value As Point)
            m_Location = value
        End Set
    End Property

    ''' <summary>
    ''' The Radius of the LED
    ''' </summary>
    ''' <value></value>
    ''' <returns></returns>
    ''' <remarks></remarks>
    Public Property Radius As Integer
        Get
            Return m_Radius
        End Get
        Set(value As Integer)
            m_Radius = value
        End Set
    End Property


#End Region

End Class

''' <summary>
''' Definition of a item displayed on the led matrix
''' </summary>
''' <remarks></remarks>
Friend Class LedMatrixItem

#Region " Private Members "

    Private m_Id As Integer
    Private m_MasterLedOn(,) As Boolean
    Private m_CurrentLocation As Point
    Private m_Direction As ItemDirection
    Private m_Speed As ItemSpeed

#End Region

#Region " Constructor "

    ''' <summary>
    ''' Default Constructor
    ''' </summary>
    ''' <remarks></remarks>
    Public Sub New()
        m_Id = -1
        m_CurrentLocation.X = 0
        m_CurrentLocation.Y = 0
    End Sub

    ''' <summary>
    ''' Constructor
    ''' </summary>
    ''' <param name="Id">Id code</param>
    ''' <param name="MasterLedOn">Led On definition</param>
    ''' <param name="InitalLocation">Initial location</param>
    ''' <param name="Direction">Direction</param>
    ''' <param name="Speed">Speed</param>
    ''' <remarks></remarks>
    Public Sub New(ByVal Id As Integer, ByVal MasterLedOn(,) As Boolean, ByVal InitalLocation As Point, ByVal Direction As ItemDirection, ByVal Speed As ItemSpeed)
        m_Id = Id
        m_MasterLedOn = MasterLedOn
        m_Direction = Direction
        m_Speed = Speed
        m_CurrentLocation = InitalLocation
    End Sub

#End Region

#Region " Public Properties "

    ''' <summary>
    ''' The Id of the Item
    ''' </summary>
    ''' <value></value>
    ''' <returns></returns>
    ''' <remarks></remarks>
    Public ReadOnly Property Id As Integer
        Get
            Return m_Id
        End Get
    End Property

    ''' <summary>
    ''' Get the current line offset of the item (Y pos)
    ''' </summary>
    ''' <value></value>
    ''' <returns></returns>
    ''' <remarks></remarks>
    Public ReadOnly Property LineOffset As Integer
        Get
            Return m_CurrentLocation.Y
        End Get
    End Property

    ''' <summary>
    ''' Get the current row offset of the item (X pos)
    ''' </summary>
    ''' <value></value>
    ''' <returns></returns>
    ''' <remarks></remarks>
    Public ReadOnly Property RowOffset As Integer
        Get
            Return m_CurrentLocation.X
        End Get
    End Property

#End Region

#Region " Public Methods "

    ''' <summary>
    ''' Get the led On definition of the item
    ''' </summary>
    ''' <returns>Led On definition of the item</returns>
    ''' <remarks></remarks>
    Public Function GetMasterLedOn() As Boolean(,)
        Return m_MasterLedOn
    End Function

    ''' <summary>
    ''' Define the led On definition of the item
    ''' </summary>
    ''' <param name="MasterLedOn">Led On definition</param>
    ''' <remarks></remarks>
    Public Sub SetLedOnArray(ByVal MasterLedOn(,) As Boolean)
        m_MasterLedOn = MasterLedOn
    End Sub

    ' ''' <summary>
    ' ''' Define the current location of the item
    ' ''' </summary>
    ' ''' <param name="Location">Location of the item</param>
    ' ''' <remarks></remarks>
    'Public Sub SetLocation(ByVal Location As Point)
    '    m_CurrentLocation = Location
    'End Sub

    ' ''' <summary>
    ' ''' Define the direction of the item
    ' ''' </summary>
    ' ''' <param name="Direction">Direction of the item</param>
    ' ''' <remarks></remarks>
    'Public Sub SetDirection(ByVal Direction As ItemDirection)
    '    m_Direction = Direction
    'End Sub

    ' ''' <summary>
    ' ''' Define the speed of the item
    ' ''' </summary>
    ' ''' <param name="Speed">Speed of the item</param>
    ' ''' <remarks></remarks>
    'Public Sub SetSpeed(ByVal Speed As ItemSpeed)
    '    m_Speed = Speed
    'End Sub

    ''' <summary>
    ''' Move the item in the led matrix according to its properties
    ''' </summary>
    ''' <param name="TickCount">Clock tick count</param>
    ''' <param name="NbCtrlLedLine">Number of lines in the matrix</param>
    ''' <param name="NbCtrLedRow">Number of rows in the matrix</param>
    ''' <remarks></remarks>
    Public Sub MoveItem(ByVal TickCount As UInteger, ByVal NbCtrlLedLine As Integer, ByVal NbCtrLedRow As Integer)

        Dim iOffset As Integer = 0

        ' Offset computations

        ' Define the offset amplitude
        Select Case m_Speed
            Case ItemSpeed.Slow
                If (TickCount Mod 2) = 0 Then
                    iOffset = 1
                End If
            Case ItemSpeed.Idle
                iOffset = 1
            Case ItemSpeed.Fast
                iOffset = 2
        End Select

        ' Any movement ?
        If iOffset = 0 Then
            Return
        End If

        ' Applies the offest according to the item direction
        Select Case m_Direction
            Case ItemDirection.Up
                m_CurrentLocation.Y -= iOffset
            Case ItemDirection.Down
                m_CurrentLocation.Y += iOffset
            Case ItemDirection.Left
                m_CurrentLocation.X -= iOffset
            Case ItemDirection.Right
                m_CurrentLocation.X += iOffset
        End Select

        ' Corrections
        If (m_CurrentLocation.X >= NbCtrLedRow) Then
            ' Exit by right
            m_CurrentLocation.X = -m_MasterLedOn.GetLength(1)
        ElseIf (m_CurrentLocation.X < -m_MasterLedOn.GetLength(1)) Then
            ' Exit by left
            m_CurrentLocation.X = NbCtrLedRow - 1
        End If

        If (m_CurrentLocation.Y >= NbCtrlLedLine) Then
            ' Exit by bottom
            m_CurrentLocation.Y = -m_MasterLedOn.GetLength(0)
        ElseIf (m_CurrentLocation.Y < -m_MasterLedOn.GetLength(0)) Then
            ' Exit by top
            m_CurrentLocation.Y = NbCtrlLedLine - 1
        End If
    End Sub

#End Region

End Class

#End Region

''' <summary>
''' Definition of the led matrix control
''' </summary>
''' <remarks></remarks>
Partial Friend Class LedMatrixControl
    Inherits Control

#Region " Private Members "

    Private m_NbLedLines As Integer   'Number of led lines
    Private m_NbLedRows As Integer  'Number of led rows
    Private m_LedSizeCoeff As Double  'Coefficent of the led diameter vs the led cell size: (0->1)
    Private m_OnBrush As SolidBrush        'Led On brush
    Private m_OffBrush As SolidBrush    'Led Off brush
    Private m_LedStyle As LedStyle   'Led style
    Private m_LedPropertyList As List(Of List(Of LedProperty)) 'List of the matrix led
    Private m_FontCollection As LedMatrixSymbolFontCollection 'Font collection of the control
    Private m_ItemList As List(Of LedMatrixItem)      'List of items displayed on the control
    Private m_ItemIdCount As Integer  'Item Id counter
    Private m_Timer As Timer  'Internal control timer
    Private m_TickCount As UInteger   'Timer ticks counting

#End Region

#Region " Constuctor "

    ''' <summary>
    ''' Default Constructor 
    ''' </summary>
    ''' <remarks></remarks>
    Public Sub New()

        SetStyle(ControlStyles.DoubleBuffer Or ControlStyles.UserPaint Or ControlStyles.AllPaintingInWmPaint, True)

        'Size
        m_NbLedLines = 16
        m_NbLedRows = 64
        m_LedSizeCoeff = 0.67

        'Color
        m_OnBrush = New SolidBrush(Color.Red)
        m_OffBrush = New SolidBrush(Color.DarkGray)

        'Style
        m_LedStyle = LedStyle.Square

        'Led collection
        m_ItemList = New List(Of LedMatrixItem)
        m_LedPropertyList = New List(Of List(Of LedProperty))

        'Timer
        m_Timer = New System.Windows.Forms.Timer()
        m_Timer.Interval = 70

        AddHandler m_Timer.Tick, AddressOf m_tTimer_Tick
        m_TickCount = 0

        'Id count
        m_ItemIdCount = -1

        'Place the leds on the control
        SetMatrixSize(m_NbLedLines, m_NbLedRows)
    End Sub

#End Region

#Region " Public Properties "

    <Category("Matrix size"), Description("Rows"), DefaultValue(64)> _
    Public Property NbLedRows As Integer
        Get
            Return m_NbLedRows
        End Get
        Set(ByVal value As Integer)
            m_NbLedRows = value
            SetMatrixSize(m_NbLedLines, m_NbLedRows)
            Invalidate()
        End Set
    End Property

    <Category("Matrix size"), Description("Lines"), DefaultValue(2)> _
    Public Property NbLedLines As Integer
        Get
            Return m_NbLedLines
        End Get
        Set(ByVal value As Integer)
            m_NbLedLines = value
            SetMatrixSize(m_NbLedLines, m_NbLedRows)
            Invalidate()
        End Set
    End Property

    <Category("Matrix size"), Description("Size coeff"), DefaultValue(0.66)> _
    Public Property SizeCoeff As Double
        Get
            Return m_LedSizeCoeff
        End Get
        Set(ByVal value As Double)
            If (value < 0) Or (value < 1) Then
                Invalidate()
            End If
            m_LedSizeCoeff = value
            SetPositionAndSize()
            Invalidate()
        End Set
    End Property

    <Category("Led Color"), Description("On"), DefaultValue("192,45,0")> _
    Public Property LedOnColor As Color
        Get
            Return m_OnBrush.Color
        End Get
        Set(ByVal value As Color)
            m_OnBrush.Color = value
            Invalidate()
            Refresh()
        End Set
    End Property

    <Category("Led Color"), Description("Off"), DefaultValue("192,111,105")> _
    Public Property LedOffColor As Color
        Get
            Return m_OffBrush.Color
        End Get
        Set(ByVal value As Color)
            m_OffBrush.Color = value
            Invalidate()
            Refresh()
        End Set
    End Property

#End Region

#Region " Public Methods "

    ''' <summary>
    ''' Set the style of the led
    ''' </summary>
    ''' <param name="p_lsLedSyle">Led style</param>
    ''' <remarks></remarks>
    Public Sub SetLedStyle(ByVal p_lsLedSyle As LedStyle)
        m_LedStyle = p_lsLedSyle
        Me.Refresh()
    End Sub

    ''' <summary>
    ''' Define the number of leds of the control
    ''' </summary>
    ''' <param name="NbLines">Number of led lines</param>
    ''' <param name="NbRows">Number of led rows</param>
    ''' <remarks></remarks>
    Public Sub SetMatrixSize(ByVal NbLines As Integer, ByVal NbRows As Integer)

        'Reset the property list
        m_LedPropertyList.Clear()

        'Add the led to the control
        'Loop on the lines
        For LineIndex As Integer = 0 To NbLines - 1

            Dim LineLedProperty As List(Of LedProperty) = New List(Of LedProperty)

            'Loop on the rows
            For RowIndex As Integer = 0 To NbRows - 1
                Dim lpNewProperty As LedProperty = New LedProperty()

                ' Add the led in the line
                LineLedProperty.Add(lpNewProperty)
            Next

            ' Add the line to the control
            m_LedPropertyList.Add(LineLedProperty)
        Next

        'Define the led of the control
        SetPositionAndSize()

    End Sub

    ''' <summary>
    ''' Place the led on the control and define their size
    ''' </summary>
    ''' <remarks></remarks>
    Private Sub SetPositionAndSize()

        Dim CellSize As Integer = 0
        Dim CellSizeFromWidth As Integer = 0
        Dim CellSizeFromHeight As Integer = 0
        Dim HeightMargin As Integer = 0
        Dim WidthMargin As Integer = 0
        Dim YLinepos As Integer = 0
        Dim XRowpos As Integer = 0

        'Select the direction size leader
        CellSizeFromWidth = Me.Width \ (m_LedPropertyList(0).Count)
        CellSizeFromHeight = Me.Height \ (m_LedPropertyList.Count)
        If CellSizeFromHeight > CellSizeFromWidth Then
            CellSize = CellSizeFromWidth
        Else
            CellSize = CellSizeFromHeight
        End If

        'Deduct the margin
        WidthMargin = (Me.Width - m_LedPropertyList(0).Count * CellSize) \ 2
        HeightMargin = (Me.Height - m_LedPropertyList.Count * CellSize) \ 2

        'Set position and size

        'Vertical adjustment  
        YLinepos = HeightMargin + CellSize \ 2

        'Loop on the lines
        For LineIndex As Integer = 0 To m_LedPropertyList.Count - 1

            'Horizontal adjustment
            XRowpos = WidthMargin + CellSize \ 2

            'Loop on the rows
            For RowIndex As Integer = 0 To m_LedPropertyList(0).Count - 1
                'Position
                Dim NewLocation As Point = m_LedPropertyList(LineIndex)(RowIndex).Location
                NewLocation.X = XRowpos
                NewLocation.Y = YLinepos
                m_LedPropertyList(LineIndex)(RowIndex).Location = NewLocation

                'Size
                m_LedPropertyList(LineIndex)(RowIndex).Radius = CInt(CellSize * m_LedSizeCoeff / 2)


                'Updates
                XRowpos += CellSize
            Next

            'Updates
            YLinepos += CellSize
        Next

        'Final refresh
        Me.Refresh()
    End Sub

    ''' <summary>
    ''' Get an item index in the item list by its Id
    ''' </summary>
    ''' <param name="Id">Id of the item</param>
    ''' <returns>Index of the item</returns>
    ''' <remarks></remarks>
    Private Function GetItemIndexFromId(ByVal Id As Integer) As Integer
        Dim Index As Integer = 0

        'Scan the item list
        For Each Item As LedMatrixItem In m_ItemList
            'Id match ?
            If Item.Id() = Id Then
                Return Index
            End If
            Index += 1
        Next

        'Fail
        Return -1
    End Function

    ''' <summary>
    ''' Move the items of the control
    ''' </summary>
    ''' <param name="TickCount">Clock ticks counting</param>
    ''' <remarks></remarks>
    Private Sub MoveItems(ByVal TickCount As UInteger)
        'On each item
        For Each Item As LedMatrixItem In m_ItemList
            'Move the item
            Item.MoveItem(TickCount, m_LedPropertyList.Count, m_LedPropertyList(0).Count)
        Next
        Me.Refresh()
    End Sub

    ''' <summary>
    ''' Get the led on array from a char defined in the font collection
    ''' </summary>
    ''' <param name="SourceChar">Ascii code of the character</param>
    ''' <returns>The corresponding led on array</returns>
    ''' <remarks></remarks>
    Private Function GetLedOnFromChar(ByVal SourceChar As Char) As Boolean(,)

        Dim Exclaim(4, 4) As Boolean

        'Look in the font collection
        For Each Symbol As LedMatrixSymbol In m_FontCollection.FontList(0).SymbolList
            'Match ?
            If Asc(SourceChar) = Symbol.Code Then
                Return Symbol.LedOnMatrix
            End If
        Next

        'Unfound symbol
        Exclaim(0, 0) = True
        Exclaim(0, 2) = True
        Exclaim(1, 0) = True
        Exclaim(1, 2) = True
        Exclaim(3, 0) = True
        Exclaim(3, 2) = True
        Return Exclaim

    End Function

    ''' <summary>
    ''' Get the led on array from a string
    ''' </summary>
    ''' <param name="SourceString">String to convert</param>
    ''' <returns>The corresponding led on array</returns>
    ''' <remarks></remarks>
    Private Function GetLedOnFromString(ByVal SourceString As String) As Boolean(,)

        Dim MaxHeightChar As Integer = 0
        Dim NbOfLedUsed As Integer = 0
        Dim LedCount As Integer = 0
        Dim LedOnChar As New List(Of Boolean(,))

        'Get the list of the ledOn array symbols form the string's char

        'Loop on the char
        For CharIndex As Integer = 0 To SourceString.Length - 1

            'Get the corresponding array
            LedOnChar.Add(GetLedOnFromChar(SourceString(CharIndex)))

            'Memorize the maximum height size of the chars
            If LedOnChar(LedOnChar.Count - 1).GetLength(0) > MaxHeightChar Then
                MaxHeightChar = LedOnChar(LedOnChar.Count - 1).GetLength(0)
            End If

            'Updates the nb of led used by the all chars
            NbOfLedUsed += LedOnChar(LedOnChar.Count - 1).GetLength(1)
        Next

        'Create the return array

        'Allocation
        Dim ReturnLedOn(MaxHeightChar, NbOfLedUsed) As Boolean

        'Loop on the char arrays
        For Each CurrentLedOn(,) As Boolean In LedOnChar

            'Loop on lines
            For LineIndex As Integer = 0 To CurrentLedOn.GetLength(0) - 1
                'Loop on rows
                For RowIndex As Integer = 0 To CurrentLedOn.GetLength(1) - 1
                    'Copy the ledOn
                    ReturnLedOn(LineIndex, LedCount + RowIndex) = CurrentLedOn(LineIndex, RowIndex)
                Next
            Next

            'Update
            LedCount += CurrentLedOn.GetLength(1)
        Next

        Return ReturnLedOn
    End Function

    ''' <summary>
    ''' Load the control font collection from an xml reader stream
    ''' </summary>
    ''' <param name="ReaderXml">Xml reader stream</param>
    ''' <returns>true for ok; false for fail</returns>
    ''' <remarks></remarks>
    Private Function LoadFontCollectionFromXmlReader(ByVal ReaderXml As XmlTextReader) As Boolean

        Dim ReturnCollection As LedMatrixSymbolFontCollection
        Dim FontList As New List(Of LedMatrixSymbolFont)

        'Init the reader
        ReaderXml.WhitespaceHandling = WhitespaceHandling.None

        'Read the file

        'While there is somthing to read
        While (ReaderXml.Read())

            Dim NewFont As LedMatrixSymbolFont
            Dim FontName As String
            Dim SymbolList As New List(Of LedMatrixSymbol)

            'End of font ?
            If Not ReaderXml.Name = "LedMatrixSymbolFont" Then
                'Next one
                Continue While
            End If

            'New font
            FontName = ReaderXml.GetAttribute("FontName")

            'Identify the symbols of the font
            While (ReaderXml.Read())

                Dim SmbCode As UInteger = 0
                Dim Description As String
                Dim LedOnMatrix(,) As Boolean
                Dim NewSymbol As LedMatrixSymbol
                Dim LedOnLines As New List(Of LedOnLine)

                'End of sybmols ?
                If Not ReaderXml.Name = "LedMatrixSymbol" Then
                    'Next one
                    Exit While
                End If

                'New symbol
                SmbCode = Convert.ToUInt32(ReaderXml.GetAttribute("SymbolCode"))
                Description = ReaderXml.GetAttribute("Description")

                'Identify the Led matrix of the symbol
                While (ReaderXml.Read())

                    Dim LineNumber As Integer = 0
                    Dim LedOn As String
                    Dim LedOnLine As LedOnLine

                    'End of Led line
                    If Not ReaderXml.Name = "LedLine" Then
                        'Next one
                        Exit While
                    End If

                    'New Led line
                    LineNumber = Convert.ToInt32(ReaderXml.GetAttribute("LineNumber"))
                    LedOn = ReaderXml.GetAttribute("LineLedOn")

                    'Add the line to the list
                    LedOnLine = New LedOnLine(LineNumber, LedOn)
                    LedOnLines.Add(LedOnLine)

                End While

                'Convert the led lines to led matrix
                LedOnMatrix = ConvertLedOnLineToLedOnMatrix(LedOnLines)

                'Create and add the symbol
                NewSymbol = New LedMatrixSymbol(SmbCode, Description, LedOnMatrix)
                SymbolList.Add(NewSymbol)

            End While

            'Create and add the font
            NewFont = New LedMatrixSymbolFont(FontName, SymbolList)
            FontList.Add(NewFont)

        End While

        'Create the return font collection
        ReturnCollection = New LedMatrixSymbolFontCollection(FontList)

        'Valid font collection ?
        If FontList.Count = 0 Then
            Return False
        ElseIf (FontList(0).SymbolList.Count = 0) Then
            Return False
        End If

        'copy the collection
        m_FontCollection = ReturnCollection
        Return True
    End Function

#End Region

#Region " Public Methods "

    ''' <summary>
    ''' Add an item built from text to the item list
    ''' </summary>
    ''' <param name="Text">New item text</param>
    ''' <param name="Location">Initial position of the new item</param>
    ''' <param name="Direction">Direction of the new item</param>
    ''' <param name="Speed">Speed of the new item</param>
    ''' <returns>Item Id</returns>
    ''' <remarks></remarks>
    Public Function AddTextItem(ByVal Text As String, ByVal Location As Point, ByVal Direction As ItemDirection, ByVal Speed As ItemSpeed) As Integer

        'Updates Id counting
        m_ItemIdCount += 1

        'New item
        Dim MyNewItem As LedMatrixItem = New LedMatrixItem(m_ItemIdCount, GetLedOnFromString(Text), Location, Direction, Speed)

        'Add the item to the list
        m_ItemList.Add(MyNewItem)

        'Return item Id
        Return m_ItemIdCount

    End Function

    ''' <summary>
    ''' Set the led On definition of an item from text
    ''' </summary>
    ''' <param name="ItemId">Id of the item</param>
    ''' <param name="Text">Text to put in the led On definition</param>
    ''' <remarks></remarks>
    Public Sub SetItemText(ByVal ItemId As Integer, ByVal Text As String)

        Dim LedOn(,) As Boolean
        Dim ItemIdx As Integer = -1

        'Get the item index
        ItemIdx = GetItemIndexFromId(ItemId)
        If ItemIdx = -1 Then
            Return
        End If

        'Convert text to led On
        LedOn = GetLedOnFromString(Text)

        'Set the new led On definition
        m_ItemList(ItemIdx).SetLedOnArray(LedOn)
        Me.Refresh()

    End Sub

    ''' <summary>
    ''' Load a font collection from the xml font file in the resources of the project
    ''' </summary>
    ''' <param name="ResourceName"></param>
    ''' <returns>true for ok; false for fail</returns>
    ''' <remarks></remarks>
    Public Function LoadFontCollectionFromResource(ByVal ResourceName As String) As Boolean
        Dim Assem As Assembly = Assembly.GetExecutingAssembly()
        Dim XmlStream As Stream = Assem.GetManifestResourceStream(ResourceName)
        Dim ReaderXml As XmlTextReader = New XmlTextReader(XmlStream)

        Return LoadFontCollectionFromXmlReader(ReaderXml)
    End Function

#End Region

#Region " Shared Methods "

    ''' <summary>
    ''' Convert a xml ledOn lines to a ledOn bool array
    ''' </summary>
    ''' <param name="LedOnLine">List of led led on line to convert</param>
    ''' <returns>LedOn line array</returns>
    ''' <remarks></remarks>
    Private Shared Function ConvertLedOnLineToLedOnMatrix(ByVal LedOnLine As List(Of LedOnLine)) As Boolean(,)

        Dim MaxLineSize As Integer = 0
        Dim MaxLineNb As Integer = 0
        Dim ReturnLedOnMatrix(,) As Boolean

        'Get the size of the matrix
        For Each Line As LedOnLine In LedOnLine
            If Line.LedOn.Length > MaxLineSize Then
                MaxLineSize = Line.LedOn.Length
            End If

            If Line.LineNo > MaxLineNb Then
                MaxLineNb = Line.LineNo
            End If
        Next

        'Creation of the return matrix
        ReDim ReturnLedOnMatrix(MaxLineNb + 1, MaxLineSize)

        'Build the matix
        For Each Line As LedOnLine In LedOnLine
            For CharIndex As Integer = 0 To Line.LedOn.Length - 1
                If Line.LedOn(CharIndex) = "#"c Then
                    ReturnLedOnMatrix(Line.LineNo, CharIndex) = True
                End If
            Next
        Next

        'Return the led on matrix
        Return ReturnLedOnMatrix
    End Function

#End Region

#Region " Event Handling "

    ''' <summary>
    ''' Timer tick event
    ''' </summary>
    ''' <param name="sender"></param>
    ''' <param name="e"></param>
    ''' <remarks></remarks>
    Private Sub m_tTimer_Tick(ByVal sender As Object, ByVal e As EventArgs)
        MoveItems(m_TickCount)
        m_TickCount = CUInt(m_TickCount + 1)
    End Sub

    ''' <summary>
    ''' Rezize event
    ''' </summary>
    ''' <param name="e"></param>
    ''' <remarks></remarks>
    Protected Overrides Sub OnResize(ByVal e As EventArgs)
        MyBase.OnResize(e)
        SetPositionAndSize()
    End Sub

    ''' <summary>
    ''' On paint event
    ''' </summary>
    ''' <param name="pe"></param>
    ''' <remarks></remarks>
    Protected Overrides Sub OnPaint(ByVal pe As PaintEventArgs)

        If pe Is Nothing Then
            Throw New ArgumentNullException("pe")
        End If

        Dim gfx As Graphics = pe.Graphics

        'Calling the base class OnPaint
        MyBase.OnPaint(pe)

        'Antialiasing
        gfx.SmoothingMode = SmoothingMode.HighQuality

        'Return if non consistant led
        If m_LedPropertyList(0)(0).Radius = 0 Then
            Return
        End If

        'Loop on the led lines
        For LineIndex As Integer = 0 To m_LedPropertyList.Count - 1

            'Loop on the led row
            For RowIndex As Integer = 0 To m_LedPropertyList(0).Count - 1

                Dim LedOn As Boolean = False

                'Loop on the display items
                For Each DisplayItem As LedMatrixItem In m_ItemList

                    'Corresponding point of the item
                    Dim LinePtForItem As Integer = LineIndex - DisplayItem.LineOffset()
                    Dim RowPtForItem As Integer = RowIndex - DisplayItem.RowOffset()

                    'Valid point ?
                    If ((LinePtForItem < 0) Or (LinePtForItem >= DisplayItem.GetMasterLedOn().GetLength(0)) Or (RowPtForItem < 0) Or (RowPtForItem >= DisplayItem.GetMasterLedOn().GetLength(1))) Then
                        'Not valid => next item
                        Continue For
                    End If

                    'OR on the cell ledOn
                    LedOn = LedOn Or DisplayItem.GetMasterLedOn()(LinePtForItem, RowPtForItem)
                Next

                'State of the led
                If LedOn = True Then
                    If m_LedStyle = LedStyle.Square Then
                        pe.Graphics.FillRectangle(m_OnBrush, _
                      m_LedPropertyList(LineIndex)(RowIndex).Location.X - m_LedPropertyList(LineIndex)(RowIndex).Radius, _
                        m_LedPropertyList(LineIndex)(RowIndex).Location.Y - m_LedPropertyList(LineIndex)(RowIndex).Radius, _
                        m_LedPropertyList(LineIndex)(RowIndex).Radius << 1, _
                        m_LedPropertyList(LineIndex)(RowIndex).Radius << 1)
                    Else
                        pe.Graphics.FillPie(m_OnBrush,
                        m_LedPropertyList(LineIndex)(RowIndex).Location.X - m_LedPropertyList(LineIndex)(RowIndex).Radius, _
                          m_LedPropertyList(LineIndex)(RowIndex).Location.Y - m_LedPropertyList(LineIndex)(RowIndex).Radius, _
                          m_LedPropertyList(LineIndex)(RowIndex).Radius << 1, _
                          m_LedPropertyList(LineIndex)(RowIndex).Radius << 1, _
                          0, 360)
                    End If
                Else
                    If m_LedStyle = LedStyle.Square Then
                        pe.Graphics.FillRectangle(m_OffBrush, _
                        m_LedPropertyList(LineIndex)(RowIndex).Location.X - m_LedPropertyList(LineIndex)(RowIndex).Radius, _
                        m_LedPropertyList(LineIndex)(RowIndex).Location.Y - m_LedPropertyList(LineIndex)(RowIndex).Radius, _
                        m_LedPropertyList(LineIndex)(RowIndex).Radius << 1, _
                        m_LedPropertyList(LineIndex)(RowIndex).Radius << 1)
                    Else
                        pe.Graphics.FillPie(m_OffBrush, _
                          m_LedPropertyList(LineIndex)(RowIndex).Location.X - m_LedPropertyList(LineIndex)(RowIndex).Radius, _
                          m_LedPropertyList(LineIndex)(RowIndex).Location.Y - m_LedPropertyList(LineIndex)(RowIndex).Radius, _
                          m_LedPropertyList(LineIndex)(RowIndex).Radius << 1, _
                          m_LedPropertyList(LineIndex)(RowIndex).Radius << 1, _
                          0, 360)
                    End If
                End If
            Next
        Next
    End Sub

#End Region

End Class

